<?php


namespace wkcurd\command;


use Symfony\Component\VarExporter\VarExporter;
use think\console\Command;
use think\console\Input;
use think\console\input\Option;
use think\console\Output;
use think\Exception;
use think\exception\ErrorException;
use think\facade\Db;
use think\validate\ValidateRule;

class Curd extends Command
{
    protected $stubList = [];

    protected function configure()
    {
        $this
            ->setName('wkcurd')
            ->addOption('table', 't', Option::VALUE_REQUIRED, 'table name', null)
            ->addOption('model', 'm', Option::VALUE_OPTIONAL, 'model name', null)
            ->addOption('controller', 'c', Option::VALUE_OPTIONAL, 'controller name', null)
            ->addOption('validate','a',Option::VALUE_OPTIONAL,'validate name',null)
            ->addOption('createTime', 'r', Option::VALUE_OPTIONAL, 'createTime', "create_time")
            ->addOption('primaryKey', 'k', Option::VALUE_OPTIONAL, 'primaryKey', "id")
            ->addOption('updateTime', 'p', Option::VALUE_OPTIONAL, 'updateTime', "update_time")
            ->addOption('is_delete', 'd', Option::VALUE_OPTIONAL, 'is_delete', "is_delete")
            ->setDescription('一键生成tp6.0的curd');
    }

    protected function execute(Input $input, Output $output)
    {
        $table = $input->getOption("table");//表名
        $model = $input->getOption('model'); //模型名称
        $controller = $input->getOption("controller");//控制器名称
        $validate = $input->getOption('validate'); //验证类
        if(empty($model) && empty($validate) && empty($controller)){
            $output->error("请选择模型,控制器还是验证类");
            exit;
        }
        try {
            list($fields,$attributes) = $this->getQeuryData($table);
            //模型不为空
            if(!empty($model)){
                list($modelNamespace, $modelName, $modelFile, $modelArr) = $this->getParseNameData($model,"model");
                $this->modelData($modelNamespace,$modelName,$modelFile,$input,$fields);
            }
            //验证类不为空
            if(!empty($validate)){
                list($validateNamespace, $validateName, $validateFile, $validateArr) = $this->getParseNameData($validate, "validate");
                $this->validateData($validateNamespace, $validateName, $validateFile,$attributes,$fields);
            }
            //控制器
            if(!empty($controller)){
                if(empty($model) || empty($validate)){
                    $output->error("创建控制器需要带上模型和验证类");
                    exit;
                }
                $this->controllerData($validateNamespace,$modelName,$modelNamespace,$validateName,$controller);
            }

        } catch (\Exception $e) {
            throw new Exception("Code: " . $e->getCode() . "\nLine: " . $e->getLine() . "\nMessage: " . $e->getMessage() . "\nFile: " . $e->getFile());
        }
        $output->info("Build Successed");
    }

    /**
     * @param $validateNamespace //验证类的命名空间
     * @param $modelName //模型类名称
     * @param $modelNamespace //模类的命名空间
     * @param $validateName //验证类的名称
     * @param $controller //控制器名称
     */
    protected function controllerData($validateNamespace,$modelName,$modelNamespace,$validateName,$controller)
    {
        //控制器类
        list($controllerNamespace, $controllerName, $controllerFile, $controllerArr) = $this->getParseNameData($controller,"controller");
        $data=[
            "validateNamespace"=>$validateNamespace,
            "model"=>$modelName,
            "modelNamespace"=>$modelNamespace,
            "controllerNamespace"=>$controllerNamespace,
            "controllerName"=>$controllerName,
            "validate"=>$validateName,
            "baseNamespace"=>$controllerNamespace
        ];
        $this->setControllerData('controller', $data, $controllerFile);
    }

    /**
     * 控制器啊类的赋值和写入文件
     * @param $name
     * @param $data
     * @param $pathname
     * @return false|int
     */
    protected function setControllerData($name,$data,$pathname)
    {
        $stubcontent = file_get_contents($this->getStub($name));
        $stubcontent = str_replace('{%validateNamespace%}', $data['validateNamespace'], $stubcontent);
        $stubcontent = str_replace('{%model%}', $data['model'], $stubcontent);
        $stubcontent = str_replace('{%modelNamespace%}', $data['modelNamespace'], $stubcontent);
        $stubcontent = str_replace('{%controllerNamespace%}', $data['controllerNamespace'], $stubcontent);
        $stubcontent = str_replace('{%controllerName%}', $data['controllerName'], $stubcontent);
        $stubcontent = str_replace('{%validate%}', $data['validate'], $stubcontent);
        //判断存不存在父类文件
        $basecontrollernamespace = str_replace('\\', '/',$data['controllerNamespace']);
        $extendfile = \think\facade\App::getRootPath().$basecontrollernamespace."/Base.php";
        if(!file_exists($extendfile)){
            $basecontent = file_get_contents($this->getStub("baseController"));
            $basecontent = str_replace('{%baseNamespace%}', $data['baseNamespace'], $basecontent);
            $this->makefile($extendfile,$basecontent);
        }
        $this->makefile($pathname,$stubcontent);
    }

    /**
     * 生成文件并写入
     * @param $pathname //文件名称
     * @param $content //文件内容
     */
    protected function makefile($pathname,$content)
    {
        if(!file_exists($pathname)){
            if (!is_dir(dirname($pathname))) {
                mkdir(dirname($pathname), 0755, true);
            }
            return file_put_contents($pathname, $content);
        }

    }

    /**
     * 模型类的创建
     * @param $modelNamespace //模型类的命名空间
     * @param $modelName //模型名称
     * @param $modelFile //模型类的存入路径
     * @param Input $input //输入的内容
     * @param $fields //对应的字段名称
     */
    protected function modelData($modelNamespace, $modelName, $modelFile,Input $input,$fields)
    {
        $data=[
            "createTime"=>$input->getOption('createTime'),
            "updateTime"=>$input->getOption('updateTime'),
            "is_delete"=>$input->getOption('is_delete'),
            "primaryKey"=>$input->getOption('primaryKey'),
            "modelName"=>$modelName,
            "modelNamespace"=>$modelNamespace,
            "fields"=>$fields
        ];
        $this->setModelData('model', $data, $modelFile);
    }

    /**
     * 完成validate类的文件写入
     * @param $validateNamespace //验证类的命名空间
     * @param  $validateName //验证类的名称
     * @param  $validateFile //路径
     * @param $attributes //字段对应的解释
     * @param $fields //字段名称
     * @throws \Symfony\Component\VarExporter\Exception\ExceptionInterface
     */
    protected function validateData($validateNamespace, $validateName, $validateFile,$attributes,$fields)
    {

        $data=[
            "validateName"=>$validateName,
            "validateNamespace"=>$validateNamespace,
            'attributes'=>$attributes,
            'rule'=>$fields
        ];

        //判断存不存在父类文件
        $extendfile = \think\facade\App::getRootPath()."extend/helper/ExtendValidate.php";
        $extndvalidatecontent = file_get_contents($this->getStub("extendvalidate"));
        $this->makefile($extendfile,$extndvalidatecontent);
        // 生成验证的文件
        $this->setValidateyData('validate', $data, $validateFile);
    }

    /**
     * 设置模型类的数据并写入文件中
     * @param $name
     * @param $data
     * @param $pathname
     * @return false|int
     */
    protected function setModelData($name, $data,$pathname)
    {
        $stubcontent = file_get_contents($this->getStub($name));
        $fields = VarExporter::export($data['fields']);

        $stubcontent = str_replace('{%modelNamespace%}', $data['modelNamespace'], $stubcontent);
        $stubcontent = str_replace('{%modelName%}', $data['modelName'], $stubcontent);
        $stubcontent = str_replace('{%is_delete%}', $data['is_delete'], $stubcontent);
        $stubcontent = str_replace('{%createTime%}', $data['createTime'], $stubcontent);
        $stubcontent = str_replace('{%updateTime%}', $data['updateTime'], $stubcontent);
        $stubcontent = str_replace('{%primaryKey%}', $data['primaryKey'], $stubcontent);
        $stubcontent = str_replace('{%fields%}',$fields, $stubcontent);
//        if (!is_dir(dirname($pathname))) {
//            mkdir(dirname($pathname), 0755, true);
//        }
        $this->makefile($pathname,$stubcontent);
        //return file_put_contents($pathname, $stubcontent);
    }

    /**
     * 通过数据库查询字段得到字段的数据
     * @param $table
     * @return array[]
     */
    protected function getQeuryData($table)
    {
        $table_name = config('database.connections.mysql.prefix').$table;
        $sql1 ="SHOW FULL COLUMNS FROM {$table_name}";
        $res1 = Db::query($sql1);
        $fields=$attributes=[];//字段内容
        foreach ($res1 as $k=>$v){
            $fields[]=$v['Field'];
            $attributes[$v['Field']]=$v['Comment'];
        }
        return [$fields,$attributes];
    }


    /**
     * 验证类的数据写入进去
     * @param $name
     * @param $data
     * @param $pathname
     * @return false|int
     * @throws \Symfony\Component\VarExporter\Exception\ExceptionInterface
     */
    protected function setValidateyData($name, $data,$pathname)
    {
        $stubcontent = file_get_contents($this->getStub($name));

        $rule  = VarExporter::export($data['rule']);
        $attributes = VarExporter::export($data['attributes']);

        $stubcontent = str_replace('{%validateNamespace%}', $data['validateNamespace'], $stubcontent);
        $stubcontent = str_replace('{%validateName%}', $data['validateName'], $stubcontent);
        $stubcontent = str_replace('{%rule%}', $rule, $stubcontent);
        $stubcontent = str_replace('{%attributes%}', $attributes, $stubcontent);
        $this->makefile($pathname,$stubcontent);
        //return file_put_contents($pathname, $stubcontent);
    }

    /**
     * 分解名称
     * @param $name
     */
    protected function getModelName($name,$method)
    {
        if (strpos($name, '@')) {
            [$app, $name] = explode('@', $name);
        } else {
            $app = 'common';
        }
        $namespace = $this->getNamespace($app,$method) . '\\' . $name;
        return $namespace;
    }

    protected function getNamespace($app,$method)
    {
        return ($app ? '\\' . $app: '').'\\'.$method ;
    }


    /**
     * 获取已解析相关信息
     * @param string $module 模块名称
     * @param string $name   自定义名称
     * @param string $table  数据表名
     * @param string $type   解析类型，本例中为controller、model、validate
     * @return array
     */
    protected function getParseNameData($name, $type)
    {
        $arr = [];
        if (strpos($name, '@')) {
            [$app, $name] = explode('@', $name);
        } else {
            $app = 'common';
        }

        $parseName = ucfirst($name);
        $parseName = $this->convertUnderline($parseName);
        $parseArr = $arr;
        array_push($parseArr, $parseName);

        $path = $this->getNamespace($app,$type);//\api\model    \common\model
        $parseNamespace = "app".$path;
        $path= str_replace('\\', '/', substr($path,1));

        $moduleDir = \think\facade\App::getAppPath() . $path . DIRECTORY_SEPARATOR;

        if($type=="validate" && stristr($parseName,'Validate')==false){
            $parseName = $parseName."Validate";
        }
        $parseFile = $moduleDir . ($arr ? implode(DIRECTORY_SEPARATOR, $arr) . DIRECTORY_SEPARATOR : '') . $parseName . '.php';

        return [$parseNamespace, $parseName, $parseFile, $parseArr];
    }


    /**
     * 写入到文件
     * @param string $name
     * @param array  $data
     * @param string $pathname
     * @return mixed
     */
    protected function writeToFile($name, $data, $pathname)
    {
        foreach ($data as $index => &$datum) {
            $datum = is_array($datum) ? json_encode($datum) : $datum;
        }
        unset($datum);
        $content = $this->getReplacedStub($name, $data);
        if (!is_dir(dirname($pathname))) {
            mkdir(dirname($pathname), 0755, true);
        }
        return file_put_contents($pathname, $content);
    }


    /**
     * 获取替换后的数据
     * @param string $name
     * @param array  $data
     * @return string
     */
    protected function getReplacedStub($name, $data)
    {
        foreach ($data as $index => &$datum) {
            $datum = is_array($datum) ? json_encode($datum) : $datum;
        }
        unset($datum);
        $search = $replace = [];
        foreach ($data as $k => $v) {
            $search[] = $k;
            $replace[] = $v;
        }
        $stubname = $this->getStub($name);
        if (isset($this->stubList[$stubname])) {
            $stub = $this->stubList[$stubname];
        } else {
            $this->stubList[$stubname] = $stub = file_get_contents($stubname);
        }
        $content = str_replace($search, $replace, $stub);
        return $content;
    }


    /**
     * 获取基础模板
     * @param string $name
     * @return string
     */
    protected function getStub($name)
    {
//        return __DIR__ . DIRECTORY_SEPARATOR . 'Crud' . DIRECTORY_SEPARATOR . $name . '.stub';
        return dirname(dirname(__DIR__)) . '/src/tpl/'.$name.".stub";

    }

    /*
 * 下划线转驼峰
 */
    private function convertUnderline($str)
    {
        $str = preg_replace_callback('/([-_]+([a-z]{1}))/i',function($matches){
            return strtoupper($matches[2]);
        },$str);
        return $str;
    }

}